;
/**
 * FormValidator类
 * 实例方法：
 * fv.verifyAll()	在提交按钮中调用，一次验证所有表单域
 * fv.verifyOnBlur()	在FormValidator实例初始化后调用一次，表示表单域在失去焦点时可验证
 * fv.customValidator()		实例初始化之后调用一次，加入自定义验证方法
 */
(function(window, jQuery){
	/**
	 * 内部类，辅助验证
	 */
	var Support = {
		/**
		 * 可以做危险字符串过滤
		 * @param pStrValue
		 * @return {*|String}
		 */
		filter: function(pStrValue){
			if( !pStrValue ) return '';
			return jQuery.trim(pStrValue
				.replace(new RegExp('\\n', 'g'), '')
				.replace(new RegExp('\\\\n', 'g'), '')
				.replace(new RegExp('\\\\r', 'g'), ''))
		},
		/**
		 * 修正IE7下require属性总为require的Bug
		 * @param pElmField
		 * @return {Boolean || String}
		 */
		isRequired: function(pElmField){
			var required;
			if (document.querySelector) {
				required = pElmField.attr("required");
				if (required === undefined || required === false) {
					return undefined;
				}
				return "required";
			} else {
				// IE6, IE7
				var outer = pElmField.get(0).outerHTML, part = outer.slice(0, outer.search(/\/?['"]?>(?![^<]*<['"])/));
				return /\srequired\b/i.test(part)? "required": undefined;
			}
		},
		isInteger: function(){

		},
		isEnChar: function(){

		},
		setError: function(pElmField, pStrErrMsg){
			var elmContainer = pElmField.parents('td');
			// 清理
			Support.clear(pElmField, elmContainer);
			// 添加错误信息
			var elmErrMsg = jQuery('<span class="msg-err">'+pStrErrMsg+'</span>');
			elmContainer.append(elmErrMsg);
			pElmField.addClass('field-inputError');
		},
		removeError: function(pElmField){
			var elmContainer = pElmField.parents('td');
			pElmField.removeClass('field-inputError');
			elmContainer.find('.msg-err').remove();
		},
		setCorrect: function(pElmField){
			var elmContainer = pElmField.parents('td');
			// 清理
			Support.clear(pElmField, elmContainer);
			pElmField.removeClass('field-inputError');
			// 添加正确信息
			var msg = arguments[1] || '';
			var elmCorrectMsg = elmContainer.find('.msg-success');
			if( elmCorrectMsg.length == 0 ) elmCorrectMsg = jQuery('<span class="msg-success"></span>');
			elmCorrectMsg.text(msg);
			elmContainer.append(elmCorrectMsg);
		},
		setPreloading: function(pElmField){
			var elmContainer = pElmField.parents('td');
			// 清理
			Support.clear(pElmField, elmContainer);
			// 添加预加载样式
			elmContainer.append('<span class="cmpt-preloading-16"></span>');
		},
		removePreloading: function(pElmField){
			var elmContainer = pElmField.parents('td');
			elmContainer.find('.cmpt-preloading-16').remove();
		},
		setPrompt: function(pElmField, pStrMsg){
			var elmContainer = pElmField.parents('td');
			// 清理
			// 添加提示信息
			var elmPrompt = elmContainer.find('.msg-prompt');
			if( elmPrompt.length == 0 ) elmPrompt = jQuery('<span class="msg-prompt"></span>');
			elmPrompt.text(pStrMsg);
			elmContainer.append(elmPrompt);
		},
		/**
		 * 将已输入的数据存储以属性形式存储在标签里，用于判断前后输入是否改变
		 * @param pElmField
		 * @param pStrValue
		 */
		storeData: function(pElmField, pStrValue){
			pElmField.val(pStrValue);
			pElmField.attr('data-store', pStrValue);
		},
		isChanged: function(pElmField){
			return !!(pElmField.attr('data-store') != pElmField.val());
		},
		/**
		 * 一次性清除正确、错误、普通提示信息
		 * @param pElmField
		 * @param pElmContainer
		 */
		clear: function(pElmField, pElmContainer){
			pElmContainer.find('.msg-err,.msg-success,.msg-prompt').remove();
		}
	};

	/**
	 * 验证器工具
	 * 注意：
	 * 1、表单域必须包含在form中
	 * 2、有name属性的表单域才会被验证，由于没有name的项目不会被提交，故不予理会
	 * HTML：
	 * <form id="Form" action="server.jsp" method="POST'>
	 *     <input type="text"
	 *     		name="amt"
	 *			class="field-text"
	 *			maxlength="10"
	 *			minlength="2"
	 *			isrequired="required"
	 *			illegal="&,*,^,%"
	 *			ajax_verify="server_ajax.jsp"/>
	 * </form>
	 * <span id="Submit">提交</span>
	 * 标准的form表单构造，input中用于验证的属性：
	 *     name="xxx"	标识该表单域需要被验证
	 *     data-type="integer"	预定义类型，默认按普通字符串验证
	 *      	目前支持的预定义包括：
	 *      	integer		大于0的正整数
	 *      	email		Email
	 *      	enchar		纯英文字符
	 *      	cnchar		纯中文字符
	 *     min="200"	如果输入的是数字，最小是200
	 *     max="500"	如果输入的是数字，最大是500
	 *     maxlength="10"	最多10个字符
	 *     minlength="2"	最少2个字符
	 *     required="required"	必填项
	 *     illegal="&,*,(,%"	不允许出现&*(%
	 *     ajax_verify="http://xxx.ooo.com/ajax_verify.php"	ajax校验地址，可选的，不作为通过验证的条件
	 * JS：
	 * var elmForm = jQuery('#Form');
	 * var fv = new FormValidator(elmForm);
	 * fv.verifyOnBlur();// 调用该方法，表单域失去焦点时验证
	 * jQuery('#Submit').on('click', function(){
	 *     if(!fv.verifyAll()) return false; // 提交之前所有表单域校验一次
	 *     elmForm.submit();
	 * });
	 * @param pElmForm
	 * @constructor
	 */
	var FormValidator = function(pElmForm){
		var me = this;
		this._elmForm = pElmForm;
		this._ajax = {}; // AJAX暂存器
		this._customValidator = {};

		// 默认执行的效果
		// 失去焦点时验证
		this.verifyOnBlur();
	};

	FormValidator.prototype = {
		/**
		 * 一次验证所有字段
		 * @return {Boolean}
		 */
		verifyAll: function(){
			var me = this;
			var elmFields = this._elmForm.find('[name]');
			// 迭代每个元素
			for(var i= 0,ln=elmFields.length; i<ln; i++){
				var elmField = elmFields.eq(i);
				// 空值验证
				me._verifyProcess(elmField);
			}
			// 检查是否全部通过验证
			return this._elmForm.find('.field-inputError').length<=0;
		},
		/**
		 * 字段在失去焦点时验证
		 */
		verifyOnBlur: function(){
			var me = this;
			this._elmForm.on('blur', '[name]', function(){
				me._verifyProcess(jQuery(this));
			}).on('focus', '[name]', function(){
					var elmField = jQuery(this),
						strName = elmField.attr('name');
					Support.removeError(elmField);
					// 如果该元素包含ajax_verify，获得焦点时取消AJAX查询
					if( elmField.attr('ajax_verify')) Support.removePreloading(elmField);
					if(me._ajax[strName]) {
						me._ajax[strName].abort();
						delete 	me._ajax[strName];
					}
			});
		},
		/**
		 * 添加自定义验证器
		 * pOValidator = {
		 *     pee: [{
		 *     		errMsg: '数据不能是xx',
		 *     		verify: function(elmField){}
		 *     	},{
		 *     		errMsg: '数据不能是oo',
		 *     		verify: function(elmField){}
		 *     	}]
		 * }
		 * pee为表单域的name，内容是数组，每个数组项是一条验证规则，errMsg表示本规则验证失败时的错误信息，
		 * verify是验证规则函数，传入的elmField参数为正在被验证的表单域，
		 * 验证通过必须返回true，失败必须返回false，若没有返回值一律按失败处理
		 * @param pOValidator
		 */
		customValidator: function(pOValidator){
			this._customValidator = pOValidator;
		},
		/**
		 * 私有方法，每一个表单域的通用验证过程
		 * @param pElmField
		 * @private
		 */
		_verifyProcess: function(pElmField){
			var me = this,
				elmField = pElmField,
				strName = elmField.attr('name'),
				strValue = Support.filter(elmField.val()),
				strTagName = pElmField[0].tagName.toLowerCase(),
				intCharLength = strValue.length;

			// 文本框项目对比上一次输入，如果为改变将不继续验证
			if(strTagName != 'select'){
				if( !Support.isChanged(elmField) && strValue.length>0 ) return;
				Support.storeData(elmField, strValue);
			}

			// 级联菜单验证
			// 级联菜单需要查找同组所有select标签，两组验证：
			// 1、最后一个是否为disabled
			// 2、最后一个是否已选择
			if(elmField.attr('role') == 'cascadeAjaxMenu'){
				var elmCMGroup = me._elmForm.find('[data-cmgroup="'+elmField.attr('data-cmgroup')+'"]');
				var elmCMLast = elmCMGroup.last();
				if ( elmCMLast.prop('disabled') ) {
					Support.setError(elmCMLast.prev('[role="cascadeAjaxMenu"]'), '请选择');
					return;
				}
				var strLastValue = elmCMLast.val();
				if( elmCMLast.val() == '-1' ){
					Support.setError(elmCMLast, '请选择');
					return;
				}
				elmCMGroup.removeClass('field-inputError');
			}

			// 空字符验证
			if( Support.isRequired(elmField) ){
				if( strTagName == 'input' ){
					// input类型
					if( intCharLength == 0 ){
						Support.setError(elmField, '不能为空');
						return;
					}
				}else if( strTagName == 'select' ){
					// select类型
					if( strValue == '-1'){
						Support.setError(elmField, '请选择');
						return;
					}
				}
			}

			// 最大字符数验证
			var strMaxLength = elmField.attr('maxlength');
			if( strMaxLength ){
				var intMaxLength = parseInt(strMaxLength, 10);
				if( intCharLength > intMaxLength ){
					Support.setError(elmField, '请勿超过'+intMaxLength+'个字，目前'+intCharLength+'个字');
					return;
				}
			}

			// 最小字符数验证
			var strMinLength = elmField.attr('minlength');
			if(strMinLength){
				var intMinLength = parseInt(strMinLength, 10);
				if( intCharLength < intMinLength){
					Support.setError(elmField, '请勿少于'+intMinLength+'个字，目前'+intCharLength+'个字');
					return;
				}
			}

			// 非法字符验证
			var strIllegal = elmField.attr('illegal');
			if( strIllegal ){
				var arrIllegal = strIllegal.split(',');
				for(var i= 0,ln=arrIllegal.length; i<ln; i++ ){
					if( strValue.indexOf(arrIllegal[i])>-1 ){
						Support.setError(elmField, '不能包含非法字符“'+arrIllegal[i]+'”');
						return;
					}
				}
			}

			// 自定义验证
			var customValidators = me._customValidator[strName];
			if( customValidators ){
				for(var i=0, ln=customValidators.length; i<ln; i++){
					var customValidator = customValidators[i];
					if( !customValidator.verify(elmField) ){
						Support.setError(elmField, customValidator.errMsg);
						return;
					}
				}
			}

			// 服务器验证
			// 这应该是最后的验证
			var strAjaxUrl = elmField.attr('ajax_verify');
			if( strAjaxUrl ){
				if(me._ajax[strName]) me._ajax[strName].abort();
				Support.setPreloading(elmField);
				me._ajax[strName] = jQuery.ajax(strAjaxUrl, {
					timeout: 3000,
					dataType: 'json',
					success: function(resp){
						Support.setPrompt(elmField, resp.msg);
					},
					error: function(XMLHttpRequest, textStatus, errorThrown){
						if( textStatus == 'timeout' ){
							Support.setError(elmField, '请求超时');
						}else if( textStatus != 'abort' ){
							Support.setError(elmField, '网络错误');
						}
					},
					complete: function(){
						Support.removePreloading(pElmField);
					}
				});
			}
			// 通过验证
			Support.setCorrect(elmField);
		}
	};
	FormValidator.prototype.constructor = FormValidator;
	window.FormValidator = FormValidator;
})(window, jQuery);